# 多线程是一种轻量级的进程，线程是操作系统能够进行运算调度的最小单位，它被包含在进程之中，一个进程可以拥有多个线程
# 这些线程在共享内存的情况下相互独立，每个线程都有自己的一组寄存器、堆栈和局部变量，线程之间的切换和调度不会引起进程的切换


# import time, random, threading
# from concurrent.futures import ThreadPoolExecutor # 线程池的执行器

# def crawl(url: str):
#     start = time.time()
#     print(f"crawling {url} started....")
#     time.sleep(random.randint(1, 5))
#     end = time.time()
#     print(f"Crawling {url} done, costs {end - start : .4f} seconds")
#     return url

# DEMO: 多线程
# if __name__ == "__main__":
#     import threading
#     print(f"{__name__} start crawling")
#     urls = ["http://www.baidu.com", "http://www.qq.com", "http://fastclass.cn"]
#     start = time.time()
#     threads = [] # 用以保留线程的list
#     for url in urls:
#         t = threading.Thread(target=crawl,args=(url,)) # 为每个url创建一个线程
#         t.start() # 启动线程
#         threads.append(t) # 将线程放入上方的list，为了保证下方的join的执行可以得到保证
#     for t in threads: # 阻塞主线程，等待所有子线程结束
#         t.join()
#     end = time.time()
#     print(f"Finished all actions, costs {end-start : .4f} seconds")



## 使用多线程，定义新的thread子类，并重写thread中的run方法（它是线程的入口）

# class CrawlThread(threading.Thread):
#     def __init__(self,url):
#         super().__init__()
#         self.url = url
#     def run(self):
#         crawl(self.url)

# if __name__ == "__main__":
#     print(f"{__name__} start crawling")
#     urls = ["http://www.baidu.com", "http://www.qq.com", "http://fastclass.cn"]
#     start = time.time()
#     threads = [] # 用以保留线程的list
#     for url in urls:
#         t = CrawlThread(url) # 为每个url创建一个线程
#         t.start() # 启动线程
#         threads.append(t) # 将线程放入上方的list，为了保证下方的join的执行可以得到保证
#     for t in threads: # 阻塞主线程，等待所有子线程结束
#         t.join()
#     end = time.time()
#     print(f"Finished all actions, costs {end-start : .4f} seconds")



# demo： 多线程线程池,复用线程池中的线程
### map方法
# if __name__ == "__main__":
#     print(f"{__name__} start crawling")
#     urls = ["http://www.baidu.com", "http://www.qq.com", "http://fastclass.cn"]
#     start = time.time()
#     # with 语句会自动开闭
#     with ThreadPoolExecutor(max_workers=2) as executor: # ThreadPoolExecutor 类创建了一个线程池; 其中已经默认设置了线程的数量（max_workers=min(32, (os.cpu_count() or 1) + 4)）
#          executor.map(crawl, urls)   # 调用 map() 方法来执行多个任务，map() 方法会自动分配任务给线程池中的线程，然后等待所有任务执行完毕
#     end = time.time()
#     print(f"Finished all actions, costs {end-start : .4f} seconds")

### submit方法
# if __name__ == "__main__":
#     print(f"{__name__} start crawling")
#     urls = ["http://www.baidu.com", "http://www.qq.com", "http://fastclass.cn"]
#     start = time.time()
#     # with 语句会自动开闭
#     with ThreadPoolExecutor() as executor: # ThreadPoolExecutor 类创建了一个线程池; 其中已经默认设置了线程的数量（max_workers=min(32, (os.cpu_count() or 1) + 4)）
#         #  executor.map(crawl, urls)   # 调用 map() 方法来执行多个任务，map() 方法会自动分配任务给线程池中的线程，然后等待所有任务执行完毕
#         futures = [executor.submit(crawl, url) for url in urls]  # 区别在于一次传递url； submit方法会有future返回值
#         for f in futures:
#             print(f.result())
#     end = time.time()
#     print(f"Finished all actions, costs {end-start : .4f} seconds")


# demo：账户操作，多线程加锁
import time, random,threading

class Account:
    def __init__(self,balance):
        self.balance = balance
        self.lock = threading.RLock()  # 创建一个可重入锁
        self.condition = threading.Condition(self.lock) # 创建一个条件变量--具有锁的作用，也有条件判断能力

    
    def withdraw(self,amount):
        with self.condition:
            while self.balance < amount: # 判断余额和取出金额
                self.condition.wait()  # 等待条件满足
            new_balance = self.balance - amount
            time.sleep(random.random())
            self.balance = new_balance
            print(f'Withdraw {amount} done, balance: {self.balance}, current thread: {threading.current_thread().name}')

        # # 手动获取锁
        # self.lock.acquire()
        # try:
        #     new_balance = self.balance - amount
        #     time.sleep(random.random())
        #     self.balance = new_balance
        # finally:
        #     # 释放锁
        #     self.lock.release()

    def deposit(self,amount):
        # 自动的获取和释放锁
        with self.condition:
            new_balance = self.balance + amount
            time.sleep(random.random())
            self.balance = new_balance
            self.condition.notify_all()  # 条件满足时，通知所有等待的线程
            print(f'Deposit {amount} done, balance: {self.balance}, current thread: {threading.current_thread().name}')        

if __name__ == '__main__':
    account = Account(1000)  # 创建一个账户，初始余额 1000
    threads = []
    # 创建 5 个线程，每个线程取钱 300 元
    for i in range(5):
        t = threading.Thread(target=account.withdraw, args=(300,))
        threads.append(t)
        t.start()
    # 创建 5 个线程，每个线程存钱 200 元
    for i in range(5):
        t = threading.Thread(target=account.deposit, args=(200,))
        threads.append(t)
        t.start()
    for t in threads:
        t.join()
    print(f'Account balance: {account.balance}')


# if __name__ == "__main__":
#     account = Account(1000) # 创建账户
#     threads = []
#     # 创建5个线程，均取钱
#     for i in range(5):
#         t = threading.Thread(target=account.withdraw, args=(200,))
#         threads.append(t)
#         t.start()  # 线程开始
#     for t in threads:
#         t.join()   # 等待线程结束
#     print(f"ALL done，got {account.balance} in my account")